<cfcomponent name="Object" hint="Generic system object who's responsibility is to provide a foundation of standard properties and/or methods that LMS system objects shall inherit.&nbsp; Not intended to be used directly, but rather for extending." >


	<!--- Properties --->


	<cfset variables.instance = StructNew() />


	<!--- Accessors --->


	<cffunction name="setId" access="public" returntype="void" output="false">
		<cfargument name="id" type="any" required="true" />
		<cfset variables.instance.id = trim(arguments.id) />
		<cfset this.id = arguments.id />
	</cffunction>
	<cffunction name="getId" access="public" returntype="any" output="false">
		<cfreturn variables.instance.id />
	</cffunction>

	<cffunction name="setKeyProperty" access="public" returntype="void" output="false">
		<cfargument name="keyProperty" type="string" required="true" />
		<cfset variables.instance.keyProperty = arguments.keyProperty />
	</cffunction>
	<cffunction name="getKeyProperty" access="public" returntype="string" output="false">
		<cfreturn variables.instance.keyProperty />
	</cffunction>

	<cffunction name="setDAOPath" access="public" returntype="void" output="false">
		<cfargument name="DAOPath" type="string" required="true" />
		<cfset variables.instance.DAOPath = arguments.DAOPath />
	</cffunction>
	<cffunction name="getDAOPath" access="public" returntype="string" output="false">
		<cfreturn variables.instance.DAOPath />
	</cffunction>

	<cffunction name="setEntityTypeId" access="public" returntype="void" output="false">
		<cfargument name="entityTypeId" type="numeric" required="true" />
		<cfset variables.instance.entityTypeId = trim(arguments.entityTypeId) />
	</cffunction>
	<cffunction name="getEntityTypeId" access="public" returntype="numeric" output="false">
		<cfreturn variables.instance.entityTypeId />
	</cffunction>

	<cffunction name="setEntityTypeCode" access="public" returntype="void" output="false">
		<cfargument name="entityTypeCode" type="string" required="true" />
		<cfset variables.instance.entityTypeCode = arguments.entityTypeCode />
	</cffunction>
	<cffunction name="getEntityTypeCode" access="public" returntype="string" output="false">
		<cfreturn variables.instance.entityTypeCode />
	</cffunction>


	<!--- Public Methods --->


	<cffunction name="init" returnType="com.revenea.root.Object" access="public" output="false" hint="Initializes the object, thereby returning an instantiation of it populated with its default properties.&nbsp; While subclasses will inherit this method, they will use their own setDefaults method to populate their subclass properties.">
		<cfargument name="keyValue" type="any" required="false" default="0" hint="" />
		<cfargument name="qLoadData" type="query" required="false" hint="" />
		<!--- Throw exception if this is a direct instantiation of the Object superclass --->
		<cfif (getMetaData(this).name EQ "com.revenea.root.Object")>
			<cfthrow type="com.revenea.Object" message="Illegal instantiation attempt on an abstract super class.  This class cannot be unit tested." detail="The Object class is not intended to be instantiated.  It is an abstract class designed to pass functionality on down to subclasses." />
		</cfif>
		<!--- Set defaults for the object, using a local temp variable --->
		<cfset setDefaults() />
		<cfif (IsDefined("arguments.keyValue") AND (arguments.keyValue NEQ 0))>
			<cfset load(arguments.keyValue) />
		<cfelseif (IsDefined("arguments.keyValue") AND (arguments.keyValue EQ 0) AND (IsDefined("arguments.qLoadData")))>
			<cfset frontLoad(arguments.qLoadData) />
		</cfif>
		<cfreturn this />
	</cffunction>

	<cffunction name="load" returnType="boolean" access="public" output="false" hint="Populates the properties of the object with values from the database corresponding to this object's key ID.&nbsp; This method is likely only useful for subclasses.&nbsp; The key ID is optional, and if not provided the system will attempt to extract it from the object itself or look for a code property to use as loading criteria.">
		<cfargument name="keyValue" type="any" required="false" default="0" hint="If using default value of zero then the class and its supporting access component should be aware of a different way to load the class." />
		<cfscript>
			var local = StructNew();
			local.qLoadData = QueryNew("");
			// Call the access component
			setId(arguments.keyValue);
			local.qLoadData = getDAO().load(this);
			// Validate the query that was returned
			validateLoadQuery(local.qLoadData, 1, "load");
			// Populate the object with the retrieved data set
			populate(local.qLoadData);
			// Return a success boolean
			return true;
		</cfscript>
	</cffunction>

	<cffunction name="save" returnType="boolean" access="public" output="false" hint="Records the object&iuml;&iquest;&frac12;s property values to the database alongside the key ID.&nbsp; This is a facade-type method that interfaces with the private add or edit methods as needed. Returns true upon success.">
		<cfargument name="oUser" type="com.revenea.account.data.User" required="true" hint="The authenticated user that all auditing data generation will be recording during manipulation operations of this object" />

		<!--- Init local temp var so all cfset commands are assignments --->
		<cfset var blnSuccess = false />

		<!--- Determine if we're adding or editing based on the ID property --->
		<cfif (getKeyValue() EQ 0)>
			<!--- Add, assigning the return value to the local ID property --->
			<cfset setKeyValue(add(arguments.oUser)) />
			<!--- Ensure return ID is on-zero --->
			<cfif (getKeyValue() EQ 0)>
				<cfthrow type="com.revenea.API.Validation" message="Add method returned a zero ID value." detail="A non-zero value should have returned from the add method in the Access component #deriveAccessComponent(this)#.  However, here we are and here is a zero.  Better take a closer look." />
			</cfif>
			<!--- Set success boolean to true, because everything worked out --->
			<cfset blnSuccess = true />
		<cfelse>
			<!--- Edit --->
			<cfset blnSuccess = edit(arguments.oUser) />
		</cfif>
		<!--- Return success boolean --->
		<cfreturn blnSuccess />
	</cffunction>

	<cffunction name="remove" returnType="boolean" access="public" output="false" hint="Deletes the object from the system.">
		<cfargument name="oUser" type="com.revenea.account.data.User" required="true" hint="The authenticated user that all auditing data generation will be recording during manipulation operations of this object" />
		<!--- Return success boolean --->
		<cfreturn getDAO().remove(this, arguments.oUser) />
	</cffunction>

	<cffunction name="getDAO" returnType="com.revenea.root.DAO" access="public" output="false" hint="Using the DAOPath attribute/property, this method will retrieve an instantiation of the DAO for this data class to use.">
		<!--- Check to see if the DAO is already instantiated and caches in the private local scope --->
		<cfif ( NOT IsDefined("variables.instance.oDAO") )>
			<!--- Not found, so init and store locally --->
			<cfset variables.instance.oDAO = CreateObject("component", getDAOPath()).init() />
		</cfif>
		<!--- Return the locally cached DAO object --->
		<cfreturn variables.instance.oDAO />
	</cffunction>

	<cffunction name="getEntityType" returnType="com.revenea.utility.data.EntityType" access="public" output="false" hint="Returns a loaded EntityType that the current object (this) represents.&nbsp; There is no set equivelent because all subclass objects should already have their static _entityTypeCode value set and can therefore retrieve the corresponding EntityType object via its getter, getEntityType().">
		<!--- Check to see if the object is already instantiated and caches in the private local scope --->
		<cfif (NOT IsDefined("variables.instance.oEntityType") )>
			<!--- Not found, so init and store locally --->
			<cfset variables.instance.oEntityType = CreateObject("component", "com.revenea.utility.data.EntityType").init(variables.instance.entityTypeId) />
		</cfif>
		<!--- Return the locally cached object --->
		<cfreturn variables.instance.oEntityType />
	</cffunction>

	<cffunction name="getKeyValue" returnType="any" access="public" output="false" hint="Returns the value of what the object has been taught it the key value for itself.">
		<!--- This is NOT a getter! --->
		<cfreturn Evaluate("get" & getKeyProperty() & "()") />
	</cffunction>
	<cffunction name="setKeyValue" returntype="void" access="public" output="false" hint="Dynamically determines the key property and assigns it a value">
		<cfargument name="nVal" type="numeric" required="true" />
		<cfset Evaluate("set" & getKeyProperty() & "(#arguments.nVal#)") />
	</cffunction>

	<cffunction name="dump" returnType="void" access="public" output="false" hint="Returns a structure containing all private non-function variables for this object.">
		<cfargument name="blnAbort" type="boolean" required="false" default="false" hint="When true, the method will halt processing via the CFABORT tag." />
		<cfdump var="#variables.instance#" />
		<cfif (arguments.blnAbort EQ true)><cfabort /></cfif>
	</cffunction>


	<!--- Private methods --->


	<cffunction name="deriveAccessComponent" returnType="string" access="private" output="false" hint="Makes a best attempt to guess the object path for the paired database access component for the current object.&nbsp; This is used for setting the default DAOPath property, and also when executing getDAO().">
		<cfset var sAccessPathList = "" />
		<cfset var nSecondToLast = 0 />
		<!--- Retrieve the object meta data --->
		<cfset var oMetaData = getMetaData(this) />
		<!--- Ensure that .name is a non-empty string --->
		<cfif (Len(oMetaData.name) EQ 0)>
			<cfthrow type="API.Validation" message="Attempt to derive Access component failed." detail="The metadata for the supplied object (name=#oMetaData.name#) does not contain enough information to derive the Access component path." />
		</cfif>
		<!--- Replace the second to last from "Data" to "Access" --->
		<cfif (ListLen(oMetaData.name, ".") GT 1)>
			<cfset nSecondToLast = (ListLen(oMetaData.name, ".") - 1) />
			<cfset sAccessPathList = ListSetAt(oMetaData.name, nSecondToLast, "access", ".") />
		</cfif>
		<!--- Return the final path list --->
		<cfreturn sAccessPathList />
	</cffunction>

	<cffunction name="add" returnType="numeric" access="private" output="false" hint="Called by the save method when the keyValue property is zero or empty string, this is how objects are added to the system's persistent storage.">
		<cfargument name="oUser" type="com.revenea.account.data.User" required="true" hint="The authenticated user that all auditing data generation will be recording during manipulation operations of this object" />
		<!--- Return newly created ID --->
		<cfreturn getDAO().add(arguments.oUser, this) />
	</cffunction>

	<cffunction name="edit" returnType="boolean" access="private" output="false" hint="Updates the object's property values in their corresponding database records.">
		<cfargument name="oUser" type="com.revenea.account.data.User" required="true" hint="The authenticated user that all auditing data generation will be recording during manipulation operations of this object" />
		<!--- Return success boolean --->
		<cfreturn getDAO().edit(this, arguments.oUser) />
	</cffunction>

	<cffunction name="validateLoadQuery" returnType="void" access="private" output="false" hint="Ensures that the provided query is supplying a proper record set from which to populate.">
		<cfargument name="qLoad" type="query" required="true" hint="" />
		<cfargument name="sCallingMethod" type="string" required="false" hint="" />
		<cfif (NOT IsQuery(arguments.qLoad))>
			<cfthrow type="API.Validation" message="Load call to component #getDAOPath()# did not return a query." detail="A query was expected back from the access component.  Without it, the populate methods for super and sub classes cannot assign values to the object." />
		</cfif>
		<cfif (arguments.qLoad.RecordCount EQ 0)>
			<cfthrow type="API.Validation.NoRecordsFound" message="Load call to component #getDAOPath()# returned a query with #arguments.qLoad.RecordCount# records." detail="A query with only one row was expected back from the access component.  If zero or more than one record has returned, there is a data or query problem." />
		</cfif>
		<cfif (arguments.sCallingMethod EQ "load")>
			<cfif (arguments.qLoad.RecordCount GT 1)>
				<cfthrow type="API.Validation.ToManyRecordsFound" message="Load call to component #getDAOPath()# returned a query with #arguments.qLoad.RecordCount# records." detail="A query with only one row was expected back from the access component.  If zero or more than one record has returned, there is a data or query problem." />
			</cfif>
		</cfif>
	</cffunction>

	<cffunction name="frontLoad" returnType="boolean" access="private" output="false" hint="Populates the properties of the object with values from the supplied query.&nbsp; This method is likely only useful for subclasses, and typically those that are held or being generated from collections.">
		<cfargument name="qLoadData" type="query" required="true" hint="Record set that will be used to populate from." />
		<cfset var qSingle = QueryNew("#arguments.qLoadData.ColumnList#") />
		<!--- Validate the query that was returned --->
		<cfset temp = validateLoadQuery(arguments.qLoadData, "frontLoad") />
		<cfset QueryAddRow(qSingle) />
		<cfloop list="#arguments.qLoadData.ColumnList#" index="i">
			<cfset QuerySetCell(qSingle, i, Evaluate("arguments.qLoadData.#i#[#arguments.qLoadData.CurrentRow#]"), 1) />
		</cfloop>
		<!--- Populate the object with the retrieved data set --->
		<cfset populate(qSingle) />
		<!--- Return a success boolean --->
		<cfreturn true />
	</cffunction>

	<cffunction name="booleanToNumeric" returnType="numeric" access="private" output="false" hint="Converts a true/false to a 1/0">
		<cfargument name="blnVal" type="boolean" required="true" hint="A Boolean value to be converted to Numeric" />
		<cfif (arguments.blnVal)>
			<cfreturn 1 />
		<cfelse>
			<cfreturn 0 />
		</cfif>
	</cffunction>

	<cffunction name="numericToBoolean" returnType="boolean" access="private" output="false" hint="Converts a 1/0 to a true/false">
		<cfargument name="nVal" type="numeric" required="true" hint="" />
		<cfif (arguments.nVal EQ 1)>
			<cfreturn true />
		<cfelseif (arguments.nVal EQ 0)>
			<cfreturn false />
		<cfelse>
			<cfthrow type="API.Validation" message="The numeric value of #arguments.nVal# couldn't be converted to a Boolean because it isn't a 1 or a 0." />
		</cfif>
	</cffunction>

	<cffunction name="populate" returnType="void" access="private" output="false" hint="Populate this entity's properties based on the provided record set.&nbsp; Population is accomplished internally, by reference, so nothing is returned.">
		<cfargument name="qSource" type="query" required="true" hint="Record set provided with which to populate the entity." />
		<!--- Set properties: none to set, ID is attended to by the subclass and auditing data was removed --->
	</cffunction>

	<cffunction name="setDefaults" returnType="void" access="private" output="false" hint="Sets all properties with accepted default values">
		<cfscript>
			// Set class properties via their setters
			setId(0);
			setKeyProperty("id");
			setDAOPath(deriveAccessComponent(this));
			// Set relational data keys directly
			variables.instance.entityTypeCode = "";
			variables.instance.entityTypeId = 0;
			// Destroy cached objects
			structDelete(variables.instance, "oEntityType");
			structDelete(variables.instance, "oDAO");
		</cfscript>
	</cffunction>


</cfcomponent>